Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

4장. 변수와 자료형

프로그램은 결국 “값을 다루는 일” 이다. 값을 담아 둘 그릇이 변수고, 그 그릇이 어떤 모양인지를 정하는 게 자료형이다.

이 장에서 Go 의 변수 선언법과 기본 자료형을 익힌다.

목표:

  • var:= 의 차이를 설명할 수 있게 되기
  • Go 의 기본 자료형 종류를 알기
  • 타입 변환은 항상 명시적이어야 한다는 점 이해하기

4.1 변수 선언

var 키워드

가장 기본은 var 키워드다. 세 가지 형태를 모두 지원한다.

var x int          // 타입만 지정 (값은 제로값)
var y int = 10     // 타입과 값 모두 지정
var z = 10         // 타입은 추론 (값으로부터)

var z = 10 처럼 타입을 생략하면 컴파일러가 우변의 값으로부터 타입을 추론한다. 이 경우 z 는 자동으로 int 가 된다.

:= 짧은 선언

함수 안에서는 := 로 더 짧게 쓸 수 있다.

x := 10
name := "Go"
  • var x = 10 과 동일한 의미다
  • var 와 타입 둘 다 생략된다
  • 단, 함수 바깥(패키지 수준)에서는 못 쓴다

둘의 차이와 언제 쓰는가

구분var:=
사용 가능 위치어디서나함수 안에서만
타입 명시가능불가 (항상 추론)
초기값 없이 선언가능 (var x int)불가

보통 함수 안에서는 := 를 쓰고, 패키지 수준 변수에는 var 를 쓴다. 초기값 없이 그릇만 만들고 싶을 때도 var 가 필요하다.

여러 변수 한 번에 선언

같은 타입이라면 콤마로 묶을 수 있다.

var a, b int             // 둘 다 int, 값은 제로값
var x, y int = 1, 2      // 둘 다 int, 각각 1과 2
a, b := 10, 20           // 짧은 선언으로도 가능

심지어 타입이 달라도 된다.

name, age := "Alice", 30

이 기능은 함수가 여러 값을 반환할 때 유용하게 쓰인다. (다중 반환은 9장에서 다룬다.)

var 블록 선언

여러 변수를 묶어서 깔끔하게 선언할 때 쓴다.

var (
    name    string
    age     int
    isAdmin bool
)

가독성도 좋고, 패키지 수준 변수를 모아 둘 때 자주 쓰는 패턴이다.

값까지 함께 줄 수도 있다.

var (
    name    = "Alice"
    age     = 30
    isAdmin = false
)

4.2 기본 자료형

Go 의 기본 자료형은 크게 네 그룹이다.

  • 정수 (integer)
  • 실수 (float)
  • 불리언 (bool)
  • 문자열 (string)

정수

이름에 비트 수가 그대로 붙는다.

타입크기범위
int81바이트-128 ~ 127
int162바이트-32,768 ~ 32,767
int324바이트약 ±21억
int648바이트약 ±9.2 × 10¹⁸
uint81바이트0 ~ 255
uint162바이트0 ~ 65,535
uint324바이트0 ~ 약 42억
uint648바이트0 ~ 약 1.8 × 10¹⁹

u 가 붙은 쪽은 unsigned, 즉 음수가 없다.

intuint 는 별도다.

  • 플랫폼에 따라 32비트 또는 64비트가 된다
  • 요즘 대부분의 환경에서는 64비트
  • 특별한 이유가 없다면 그냥 int 를 쓰면 된다

byte 와 rune

이 두 타입은 사실 정수의 별명(alias) 이다.

  • byte = uint8
  • rune = int32

byte 는 “한 바이트” 라는 의미를 강조할 때, rune 은 “한 글자(유니코드)” 라는 의미를 강조할 때 쓴다. 자세한 내용은 6장에서 다룬다.

실수

타입크기비고
float324바이트정밀도 약 7자리
float648바이트정밀도 약 15자리

특별한 이유가 없다면 float64 를 쓴다.

var pi float64 = 3.14159
e := 2.71828           // 실수 리터럴은 기본 float64

불리언

참(true) / 거짓(false) 두 값만 가진다.

var ok bool = true
done := false

C 와 달리 정수 0/1 과 자동으로 교환되지 않는다. 조건문에는 반드시 bool 만 들어간다.

문자열

문자열은 string 타입이다.

var greeting string = "Hello"
name := "Go"

큰따옴표로 감싸야 하며, 작은따옴표는 다른 의미다 (단일 문자 = rune). 자세한 내용은 6장에서 다룬다.

빠른 요약

그룹대표 타입예시 값
정수int42, -7
실수float643.14, -0.5
불리언booltrue, false
문자열string"Hello"

4.3 상수

값이 한 번 정해지면 절대 바뀌지 않는 것은 상수(constant) 다. const 키워드로 선언한다.

const Pi = 3.14159
const Greeting = "Hello"
  • 컴파일 시점에 값이 고정된다
  • 실행 중에 바꿀 수 없다
  • 함수 호출 결과처럼 런타임에야 정해지는 값은 못 쓴다

타입을 지정할 수도 있다

const MaxUsers int = 100

지정하지 않으면 “타입 없는 상수” 로 남는다. 필요한 곳에서 자동으로 알맞은 타입으로 바뀌어 유연하게 동작한다.

const 블록

var 와 마찬가지로 블록으로 묶을 수 있다.

const (
    Pi       = 3.14159
    Greeting = "Hello"
    MaxUsers = 100
)

iota 맛보기

연속된 상수를 깔끔하게 정의하는 방법이 있다. iota 라는 특별한 식별자다.

const (
    Sunday    = iota  // 0
    Monday            // 1
    Tuesday           // 2
    Wednesday         // 3
    Thursday          // 4
    Friday            // 5
    Saturday          // 6
)
  • iotaconst 블록 안에서 0부터 시작
  • 한 줄 내려갈 때마다 1씩 증가
  • 다른 언어의 enum (열거형) 흉내를 낼 때 자주 쓴다

지금은 “이런 게 있다” 정도만 알아 두면 된다.


4.4 타입 변환

Go 는 다른 언어와 비교해 타입 변환에 매우 엄격하다.

묵시적 변환이 전혀 없다. 다른 타입끼리 섞으려면 반드시 직접 변환해야 한다.

명시적 변환

타입(값) 형태로 변환한다.

var i int = 10
var f float64 = float64(i)   // int → float64
var u uint = uint(f)         // float64 → uint

이걸 빠뜨리면 컴파일 에러가 난다.

var i int = 10
var f float64 = i  // 에러: cannot use i (int) as float64

같은 정수 계열이라도 그렇다.

var a int32 = 10
var b int64 = a  // 에러: int32 와 int64 는 다른 타입

처음엔 번거롭게 느껴지지만, “의도하지 않은 변환으로 인한 버그” 가 원천 차단된다.

정수 ↔ 실수 변환

실수를 정수로 바꾸면 소수점 아래는 잘려 나간다. 반올림이 아니라 그냥 버린다.

var f float64 = 3.9
var i int = int(f)   // i 는 3 (4가 아님)

음수도 마찬가지로 0 쪽으로 잘린다.

var f float64 = -3.9
var i int = int(f)   // i 는 -3

string(z) — 헷갈리는 케이스

정수에서 문자열로 변환할 때 특히 주의해야 한다.

var n int = 65
var s string = string(n)

s 는 무엇이 될까? 직관적으로는 "65" 가 될 것 같지만, 실제로는 "A" 가 된다.

Go 는 string(정수) 를 “그 정수를 유니코드 코드 포인트로 해석” 한다. 65 는 알파벳 A 의 유니코드 값이다.

숫자를 문자열로 바꾸고 싶을 땐 다른 도구를 써야 한다.

import "strconv"

n := 65
s := strconv.Itoa(n)   // s 는 "65"

strconv 패키지는 27장에서 자세히 다룬다. 지금은 “string(숫자) 는 위험하다” 정도만 기억하자.


4.5 제로값 (zero value)

Go 에서는 변수를 선언만 하고 값을 주지 않아도 “쓰레기 값” 이 들어가지 않는다.

타입별로 정해진 기본값이 자동으로 들어간다. 이걸 제로값 이라고 부른다.

타입별 제로값

타입제로값
정수 (int, int64 등)0
실수 (float32, float64)0.0
불리언 (bool)false
문자열 (string)"" (빈 문자열)
포인터, 인터페이스, 함수, 채널, 맵, 슬라이스nil
var n int       // n 은 0
var f float64   // f 는 0.0
var ok bool     // ok 는 false
var s string    // s 는 ""

C/C++ 의 쓰레기값 문제가 없다

C 나 C++ 에서는 지역 변수를 초기화 없이 선언하면 이전에 그 메모리에 남아 있던 값이 그대로 보인다. “운 좋으면 0, 운 나쁘면 이상한 숫자” 라 디버깅하기 어려운 버그의 단골 원인이었다.

Go 는 이 문제를 언어 차원에서 막는다. 선언만 했다면 항상 동일한 제로값이 들어 있다.

var count int
count++          // 안전. count 는 1

처음 만났을 때 어색할 수 있지만, 하나만 기억하면 된다.

선언했다면 이미 값이 있다. 그 값은 그 타입의 “비어 있는 상태” 다.


4.6 정리

  • 변수 선언은 var 또는 := 로 한다
    • := 는 함수 안에서만 쓸 수 있다
  • 기본 자료형은 정수 / 실수 / 불리언 / 문자열 네 그룹
    • 특별한 이유가 없으면 int, float64 를 쓴다
  • 상수는 const 로 선언, 컴파일 시점에 고정된다
  • 타입 변환은 항상 명시적이다 (int(x), float64(y))
    • string(숫자) 는 유니코드 코드 포인트로 해석되니 주의
  • 초기화하지 않은 변수는 자동으로 제로값을 가진다

자료형이 갖춰졌으면 그 값들을 가지고 계산을 해 봐야 한다. 다음 장에서는 산술, 비교, 논리 같은 연산자들을 다룬다.